﻿#region using statements

using System.Collections.Generic;

using AeonOfStrife.Combat;
using AeonOfStrife.Misc;
using AeonOfStrife.Objects.Units;

using AeonOfStrifeDatatypes.Combat;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using NGen;
using NGen.Managers.GameObject;
using NGen.Systems.Alarms;

#endregion

namespace AeonOfStrife.Objects.Castle
{
	public class Castle : GameObject2D, IAlarmListener<CastleAlarmType>, IAttackable {

		#region local fields

		protected readonly AlarmSystem<CastleAlarmType> m_alarms = new AlarmSystem<CastleAlarmType>();

		protected readonly List<CastleAttackPoint> m_lAttackPoints = new List<CastleAttackPoint>();

		protected readonly AlarmObject<CastleAlarmType> m_spawnAlarm = new AlarmObject<CastleAlarmType>(0, CastleAlarmType.SPAWN_UNITS);

		protected readonly AlarmObject<CastleAlarmType> m_updateAlarm = new AlarmObject<CastleAlarmType>(0, CastleAlarmType.UPDATE_POSITIONS);
		protected readonly TeamType m_team;

		protected readonly List<string> m_unitsToSpawn = new List<string>();

		protected float m_insideWallXPosition;

		protected const float m_spawnDelay = 15f;

		protected const float m_updateDelay = 5f;

		protected IUnitManager m_unitManager;

		#endregion

		#region constructor

		public Castle(TeamType p_team) {
			m_team = p_team;
		}

		#endregion

		#region IGameObject2D implementation

		protected override void _Initialize() {
			m_texture = GameEngine.GameContent.Load<Texture2D>("Content\\Textures\\Castle\\Castle");
			m_textureOffset.X = -m_texture.Width / 2f;
			m_spawnAlarm.AlarmEvent += OnAlarmEvent;
			m_spawnAlarm.TimeRemaning = 0.1f;
			m_alarms.Add(m_spawnAlarm);
			m_updateAlarm.AlarmEvent += OnAlarmEvent;
			m_updateAlarm.TimeRemaning = 0.1f;
			m_alarms.Add(m_updateAlarm);
			m_unitManager = GameEngine.GetService<IUnitManager>();
			m_zDepth = 0.02f;

			var pathData = GameEngine.GetService<PathfindingData>();

			var width = (m_texture.Width / pathData.CellSize.X) - 1;
			var startingPoint = pathData.ConvertScreenToCellCoord(m_position + m_textureOffset);
			var startingX = m_team == TeamType.LEFT ? startingPoint.X + (int) width : startingPoint.X;
			m_insideWallXPosition = pathData.ConvertCellCoordToScreenCoord(new Point(startingX + 2, startingPoint.Y)).X;
			if(m_team == TeamType.RIGHT) {
				m_insideWallXPosition -= pathData.CellSize.X * 1.25f;
			}


			//set the pathfinding cells covered by the castle to a high value to avoid pathfinding through them
			for(var x = startingPoint.X; x < width + startingPoint.X; x++) {
				for(var y = 0; y < pathData.GridSize.Y; y++) {
					pathData.CellPassableMap[x+1, y] = true;
				}
			}

			var pointStartY = m_position.Y + m_texture.Height * 0.4f;
			var startPointSeparationSpace = m_texture.Height * 0.6f / Constants.c_CASTLE_ATTACK_POINT_COUNT;

			for(var i = 0; i < Constants.c_CASTLE_ATTACK_POINT_COUNT; i++) {
				var point = new CastleAttackPoint(
					this, 
					new Vector2(m_insideWallXPosition, pointStartY + startPointSeparationSpace * i));
				m_lAttackPoints.Add(point);
			}
		}

		protected override void _Update(GameTime p_gameTime) {
			m_alarms.Update(p_gameTime);
		}
		
		protected override void _Cleanup() {}

		#endregion

		#region IAlarmListener implementation

		public void OnAlarmEvent(CastleAlarmType p_message) {
			switch(p_message) {
				case CastleAlarmType.SPAWN_UNITS:
					m_spawnAlarm.TimeRemaning = m_spawnDelay;
					m_alarms.Add(m_spawnAlarm);
					SpawnUnits();
					break;
				case CastleAlarmType.UPDATE_POSITIONS:
					m_unitManager.UpdateUnits(m_team);
					m_updateAlarm.TimeRemaning = m_updateDelay;
					m_alarms.Add(m_updateAlarm);
					break;
			}
		}

		#endregion

		#region IAttackable implementation

		public event UnitDeathEvent ThrowDeathEvent;

		public int Hp { get; protected set; }

		public Vector2 Position {
			get { return m_position; }
		}

		public DamageValues Defense { get; protected set; }

		public EnemyPriority EnemyPriority {
			get { return EnemyPriority.LOW; }
			set { }
		}

		public void OnAttacked(IAttacker p_attacker, DamageValues p_damage) {
			Hp -= DamageValues.GetResolvedCombatDamage(Defense, p_damage);
		}

		public void RegisterAttacker(IAttacker p_attacker) {
			ThrowDeathEvent += p_attacker.OnTargetDeath;
		}

		public void UnregisterAttacker(IAttacker p_attacker) {
			ThrowDeathEvent -= p_attacker.OnTargetDeath;
		}

		#endregion

		#region local methods

		private void SpawnUnits() {
			//TODO create units!
			if(m_unitManager.GetUnitCount(m_team) > 100) {
				return;
			}

			for(var i = 0; i < 1; i++) {
				Unit unit = GameEngine.GetService<IUnitManager>().CreateUnit("testRanged", m_team);
				unit.m_position.X = m_insideWallXPosition;
				unit.m_position.Y = m_lAttackPoints[GameEngine.Random.Next(m_lAttackPoints.Count)].Position.Y;
				unit.m_textureTint = m_team == TeamType.LEFT ? new Color(1f, 0.5f, 0.5f, 1f) : new Color(0.5f, 0.5f, 1f, 1f);
				GameEngine.GetService<IManagerGameObjects>().AddObject(unit);
			}
		}

		public CastleAttackPoint GetRandomAttackPoint() {
			return m_lAttackPoints[GameEngine.Random.Next(m_lAttackPoints.Count)];
		}

		#endregion
	}
}